package com.example.springmvc.servlet;

/**
 * @version 1.0
 * @Author 彭彦彬
 * @Date 2025/4/8 21:48
 * @注释
 */
import com.example.springmvc.annotation.*;
import com.example.springmvc.handler.HandlerMapping;
import com.example.springmvc.handler.HandlerAdapter;
import com.example.springmvc.view.ViewResolver;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DispatcherServlet extends HttpServlet {

    private Map<String, Object> controllerMap = new HashMap<>();
    private List<HandlerMapping> handlerMappings = new ArrayList<>();
    private Map<HandlerMapping, HandlerAdapter> handlerAdapters = new HashMap<>();
    private ViewResolver viewResolver;

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 1. 扫描包路径
        scanPackage("com.example.springmvc");
        // 2. 初始化 Bean 容器
        initBeans();
        // 3. 初始化 HandlerMapping
        initHandlerMappings();
        // 4. 初始化 ViewResolver
        initViewResolver();
    }

    private void scanPackage(String basePackage) {
        URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/"));
        File dir = new File(url.getFile());
        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                scanPackage(basePackage + "." + file.getName());
            } else {
                String className = basePackage + "." + file.getName().replace(".class", "");
                try {
                    Class<?> clazz = Class.forName(className);
                    if (clazz.isAnnotationPresent(Controller.class)) {
                        controllerMap.put("/" + clazz.getSimpleName(), clazz.newInstance());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void initBeans() {
        for (Object bean : controllerMap.values()) {
            Field[] fields = bean.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    try {
                        field.setAccessible(true);
                        field.set(bean, getBean(field.getType()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private Object getBean(Class<?> type) {
        for (Object bean : controllerMap.values()) {
            if (type.isAssignableFrom(bean.getClass())) {
                return bean;
            }
        }
        return null;
    }

    private void initHandlerMappings() {
        for (Map.Entry<String, Object> entry : controllerMap.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            String baseUrl = "";
            if (clazz.isAnnotationPresent(RequestMapping.class)) {
                baseUrl = clazz.getAnnotation(RequestMapping.class).value();
            }
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(RequestMapping.class)) {
                    String url = baseUrl + method.getAnnotation(RequestMapping.class).value();
                    handlerMappings.add(new HandlerMapping(entry.getValue(), method, url));
                }
            }
        }
    }

    private void initViewResolver() {
        viewResolver = new ViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        uri = uri.replace(contextPath, "");

        HandlerMapping handlerMapping = null;
        for (HandlerMapping mapping : handlerMappings) {
            if (mapping.getUrl().equals(uri)) {
                handlerMapping = mapping;
                break;
            }
        }

        if (handlerMapping == null) {
            resp.getWriter().write("404 Not Found");
            return;
        }

        HandlerAdapter handlerAdapter = handlerAdapters.get(handlerMapping);
        if (handlerAdapter == null) {
            handlerAdapter = new HandlerAdapter();
            handlerAdapters.put(handlerMapping, handlerAdapter);
        }

        try {
            Object result = handlerAdapter.handle(req, resp, handlerMapping);
            if (result instanceof String) {
                String viewName = (String) result;
                String viewPath = viewResolver.resolveViewName(viewName);
                req.getRequestDispatcher(viewPath).forward(req, resp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}